<template><div><h1 id="原型与原型链" tabindex="-1"><a class="header-anchor" href="#原型与原型链" aria-hidden="true">#</a> 原型与原型链</h1>
<!--  -->
<h2 id="前提" tabindex="-1"><a class="header-anchor" href="#前提" aria-hidden="true">#</a> 前提</h2>
<p>首先要明确：</p>
<ul>
<li>
<p>所有的对象都是通过<code v-pre>new 函数</code>创建的</p>
</li>
<li>
<p>所有的函数也是一个对象</p>
<ul>
<li>函数中可以有属性</li>
</ul>
</li>
<li>
<p>所有对象都是引用类型</p>
</li>
</ul>
<figure><img src="/assets/studyNote/prototype/prototype1.png" alt="普通函数是通过 new 函数创建的" tabindex="0" loading="lazy"><figcaption>普通函数是通过 new 函数创建的</figcaption></figure>
<p>比如：</p>
<div class="language-javascript line-numbers-mode" data-ext="js"><pre v-pre class="language-javascript"><code><span class="token keyword">const</span> fun <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> obj <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> arr <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Array</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>当你判断 new 后面的东西，得到的是 <code v-pre>function</code></p>
<div class="language-javascript line-numbers-mode" data-ext="js"><pre v-pre class="language-javascript"><code>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">typeof</span> Function<span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">typeof</span> Object<span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">typeof</span> Object<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 结果都是 function</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>在我们日常开发过程中我们也许会这样写：</p>
<div class="language-javascript line-numbers-mode" data-ext="js"><pre v-pre class="language-javascript"><code><span class="token keyword">const</span> <span class="token function-variable function">fun</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> arr <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>()=&gt;{}、{}、[]这些其实是 js 提供的语法糖，背后的逻辑其实就是 new Function()、new Object()、new Array()</p>
<p>那么问题来了：</p>
<ul>
<li>所有的函数都是 (new Function) 产生的，所有的普通对象都是 (new 函数) 产生的，那么 Function 又是哪里来的呢？？？</li>
</ul>
<p><code v-pre>Function</code>比较特殊，它是在 JS 引擎启动的时候就直接放在内存里面的，不通过任何东西来创建。</p>
<h2 id="原型-prototype" tabindex="-1"><a class="header-anchor" href="#原型-prototype" aria-hidden="true">#</a> 原型：prototype</h2>
<p>原型：所有函数都有一个属性：prototype，称之为函数原型。</p>
<p>当创建函数的同时，JS 会自动给所有的函数加上一个 prototype 属性</p>
<p>默认情况下 prototype 是一个对象</p>
<p>默认情况下 prototype 中有一个属性：constructor，它也是一个对象，而这个对象的 this 指向，指向的是函数本身。</p>
<figure><img src="/assets/studyNote/prototype/prototype2.png" alt="原型的constructor指向函数本身" tabindex="0" loading="lazy"><figcaption>原型的constructor指向函数本身</figcaption></figure>
<p>所以当你调用 <code v-pre>fun.prototype.constructor()</code> 与直接调用 <code v-pre>fun()</code> 得到的结果是一样的</p>
<div class="language-javascript line-numbers-mode" data-ext="js"><pre v-pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">test</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'test'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">test</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 输出 test</span>
test<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 输出 test</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>test <span class="token operator">===</span> test<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// true</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="隐式原型-proto" tabindex="-1"><a class="header-anchor" href="#隐式原型-proto" aria-hidden="true">#</a> 隐式原型：<strong>proto</strong></h2>
<p>所有的对象都有一个属性：__proto__，称之为隐式原型。</p>
<p><strong>函数一定是一个对象，但是对象不一定是函数</strong>，所以在函数里面既有 __proto__ 也有 prototype，而普通对象里面没有 prototype ，有 __proto__</p>
<p>默认情况下，隐式原型指向创建该对象的函数的原型。</p>
<figure><img src="/assets/studyNote/prototype/prototype3.png" alt="隐式原型的指向" tabindex="0" loading="lazy"><figcaption>隐式原型的指向</figcaption></figure>
<div class="language-javascript line-numbers-mode" data-ext="js"><pre v-pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">test</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">const</span> obj <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">test</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>obj<span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> test<span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// true</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="hint-container note">
<p class="hint-container-title">注</p>
<p>我们知道每个函数都有一个 name 属性</p>
<p>现在有一段代码：</p>
<div class="language-javascript line-numbers-mode" data-ext="js"><pre v-pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token number">0.5</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
		<span class="token keyword">return</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
		<span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">const</span> obj <span class="token operator">=</span> <span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>请问：如何得到创建 obj 的构造函数的名称？？？</p>
<div class="language-javascript line-numbers-mode" data-ext="js"><pre v-pre class="language-javascript"><code>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>obj<span class="token punctuation">.</span><span class="token class-name">__proto__</span><span class="token punctuation">.</span>constructor<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div></div></div><p>普通对象的隐式原型指向创建该对象的函数的原型，所以<code v-pre>obj.__proto__</code>就是创建它的函数的原型。</p>
<p>而函数的原型里面的<code v-pre>constructor</code>又指向函数本身，所以<code v-pre>obj.__proto__constructor</code>就是创建这个对象的函数</p>
<p>继而<code v-pre>obj.__proto__.constructor.name</code>就拿到了创建 obj 的构造函数的名称了</p>
</div>
<p>那我们想给所有的对象添加一个函数或者属性该怎么添加呢？</p>
<p>也许我们会这像做：</p>
<div class="language-javascript line-numbers-mode" data-ext="js"><pre v-pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">User</span><span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> age</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
	<span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age<span class="token punctuation">;</span>
	<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function-variable function">get</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
		console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>age<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">岁</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">const</span> u1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> u2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>这样写会有一个不好的地方，就是我们的<code v-pre>get</code>函数是使用 User 创建出来的每个对象都会有的函数，并且功能都是一样的。</p>
<p>当你输出<code v-pre>console.log(u1.get === u2.get)</code>时，你会发现是<code v-pre>false</code>这意味着他们是不同的两个函数。这样会增加对内存无意义的消耗。</p>
<p>也就是说当我们每次创建一个对象时，都会重新开辟一个新的内存空间。</p>
<p>这时候我们就可以将这个函数添加到<code v-pre>User</code>构造函数的原型上：</p>
<div class="language-javascript line-numbers-mode" data-ext="js"><pre v-pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">User</span><span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> age</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
	<span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token class-name">User</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">get</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token comment">// 这里的this会指向调用该函数的对象</span>
	console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>age<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">岁</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>现在输出<code v-pre>console.log(u1.get === u2.get)</code>时，你会发现是<code v-pre>true</code>，也就是说他们是同一个函数，</p>
<p>访问一个对象的成员时：</p>
<ol>
<li>看该对象自身是否拥有该成员，如果有直接使用。</li>
<li>看该对象的隐式原型否拥有该成员，如果有直接使用。</li>
<li>在原型链上依次向上查找。</li>
</ol>
<p>当你输出<code v-pre>console.log(u1)</code>或者<code v-pre>console.log(u1)</code>时会发现除了<code v-pre>name</code>和<code v-pre>age</code>之外并没有<code v-pre>get</code>函数。因为它在对象的隐式原型上。前面已经说过了，对象的隐式原型指向创建该对象的函数原型，所以输出<code v-pre>console.log(u1.__proto__)</code>或者<code v-pre>console.log(u1.__proto__)</code>，就可以看到<code v-pre>get</code>函数了。并且它是可以直接调用的：<code v-pre>u1.get()</code>或<code v-pre>u2.get()</code>。</p>
<p>所以当你在<code v-pre>Array.prototype</code>或<code v-pre>Object.prototype</code>添加一个方法或者属性时，你会发现所有的数组或对象都可以使用这个方法或者属性。</p>
<p>我们把这种操作称为猴子补丁。</p>
<p>猴子补丁：在函数原型中加入成员，以增强对像的功能。猴子补丁会导致原型污染，使用需谨慎。</p>
<h2 id="原型链" tabindex="-1"><a class="header-anchor" href="#原型链" aria-hidden="true">#</a> 原型链</h2>
<p>每个对象它都有一个隐式原型，隐式原型的指向所形成的链条，就叫做原型链。</p>
<div class="language-javascript line-numbers-mode" data-ext="js"><pre v-pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">fun</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">const</span> obj <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">fun</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>obj<span class="token punctuation">.</span>__proto__<span class="token punctuation">.</span>__proto__<span class="token punctuation">.</span>__proto__<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><figure><img src="/assets/studyNote/prototype/prototype4.png" alt="链条全貌" tabindex="0" loading="lazy"><figcaption>链条全貌</figcaption></figure>
<div class="hint-container warning">
<p class="hint-container-title">注意</p>
<ul>
<li>Funtion 的__proto__指向自身的 prototype。</li>
<li>Object 的 prototype 的__proto__指向 null
:::</li>
</ul>
</div>
</div></template>


